#ifndef NET_BUFFER_H
#define NET_BUFFER_H

#include "net/endian.h"
#include "utility/Copyable.h"

#include <string>
#include <vector>

/// @brief A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer
/// @code
/// +-------------------+----------------+----------------+
/// | prependable_bytes | readable_bytes | writable_bytes |
/// |                   |   (CONTENT)    |                |
/// +-------------------+----------------+----------------+
/// |                   |                |                |
/// 0      <=     _indexReader <=  _indexWriter    <=    size
class Buffer final {
private:
    static constexpr std::size_t _INIT_SIZE = 1 << 10;
    static constexpr std::size_t _PREPEND_SIZE = 1 << 3;

public:
    using Container = std::vector<char>;
    using size_type = Container::size_type;

    static constexpr auto CRLF = "\r\n";
    static constexpr auto CRLF_SIZE = 2;

    explicit Buffer(std::size_t initSize = _INIT_SIZE);
    ~Buffer();

    COPYABLE(Buffer);

    const char *begin_write() const { return &_buffer.at(_indexWriter); }
    size_type preprendable_bytes() const { return _indexReader; }
    size_type readable_bytes() const { return _indexWriter - _indexReader; }
    size_type writable_bytes() const { return _buffer.size() - _indexWriter; }

    void append(const char *data, size_type size);
    void append(const void *data, size_type size) {
        append(static_cast<const char *>(data), size);
    }
    void append(const std::string &str) {
        append(str.data(), str.size());
    }
    void ensure_writable_bytes(size_type size);
    const char *find_CRLF() const { return find_CRLF(peek()); }
    const char *find_CRLF(const char *start) const;
    void has_written(size_type size);
    const char *peek() const { return &_buffer.at(_indexReader); }
    char peek_char() const;
    uint16_t peek_uint16() const;
    uint32_t peek_uint32() const;
    uint64_t peek_uint64() const;
    void prepend(const void *data, size_type size);
    char read_char();
    ssize_t read_fd(int fd, int &savedErrno);
    uint16_t read_uint16();
    uint32_t read_uint32();
    uint64_t read_uint64();
    void retrieve();
    void retrieve(size_type size);
    std::string retrieve_as_string(size_type size);
    std::string retrieve_as_string() {
        return retrieve_as_string(readable_bytes());
    }
    template<typename T> void retrieve_type() { retrieve(sizeof(T)); }
    void retrieve_until(const char *end);
    void swap(Buffer &rhs) noexcept;



private:
    Container _buffer;
    size_type _indexReader;
    size_type _indexWriter;
};


#endif